/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package drt;

import evaluation.Graph;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import logic.ComplexType;
import logic.Type;
import util.Pair;

/**
 *
 * @author phong
 */
public class DrtLambdaExpression extends DrtStandAloneExpression {
    
    DrtVariable var;
    DrtExpression term;
    
    public DrtLambdaExpression(DrtVariable var, DrtExpression term) {
        this.var = var;
        this.term = term;
    }
    
    public static DrtLambdaExpression make(List<DrtVariable> vars, DrtExpression term) {
        DrtExpression f = term;
        for (int i = vars.size()-1; i >= 0; i--) {
            f = new DrtLambdaExpression(vars.get(i), f);
        }
        return (DrtLambdaExpression)f;
    }
    
    public Pair<List<DrtVariable>, DrtExpression> getComponents() {
        DrtExpression f = this.getTerm();
        List<DrtVariable> vars = new LinkedList<DrtVariable>();
        vars.add(this.var);
        while (f instanceof DrtLambdaExpression) {
            DrtLambdaExpression lam = (DrtLambdaExpression)f;
            vars.add(lam.var);
            f = lam.getTerm();
        }
        return new Pair<List<DrtVariable>, DrtExpression>(vars, f);
    }
    
    public DrtVariable getVar() {
        return this.var;
    }
    
    public DrtExpression getTerm() {
        return this.term;
    }
    
    @Override
    public Type getType() {
        return new ComplexType(var.getType(), term.getType());
    }
    
    @Override 
    public void setType(Type t) {
        if (!(t instanceof ComplexType))
            throw new Error("incorrect tyep");
        ComplexType type = (ComplexType)t;
        
        if (var.getType().equals(Type.UNKNOWN_TYPE)) {
            if (type.getFirst().equals(Type.ENTITY_TYPE)) {
                DrtVariable oldVar = this.var;
                this.var = new DrtEntityVariable(oldVar.getName());
                this.term = this.term.replace(oldVar, this.var);
            }
            else 
                this.var.setType(type.getFirst());
        }
        else 
            throw new Error("cannot change type");
        
        this.term.setType(type.getSecond());
    }
    
    
    @Override
    public void getRefs(LinkedHashSet<DRSReferent> refs) {
        this.term.getRefs(refs);
    }
    
    @Override
    public void getAllVariables(LinkedHashSet<DrtVariable> vars) {
        this.term.getAllVariables(vars);
        vars.add(this.var);
    }
    
    @Override
    public DrtExpression simplify() {
        return new DrtLambdaExpression(var, term.simplify());
    }
    
    @Override
    public DrtLambdaExpression replace(DrtVariable var, DrtExpression exp) {
        if (this.var.equals(var)) {
            return new DrtLambdaExpression((DrtVariable)exp, term.replace(var, exp));
        }
        else 
            return new DrtLambdaExpression(this.var, term.replace(var, exp));
            
    }
    
    @Override
    public boolean equals(Object o) {
        if (o instanceof DrtLambdaExpression) {

            DrtLambdaExpression that = (DrtLambdaExpression)o;
            
            // alpha-convert
            LinkedHashSet<DrtVariable> thisVars = new LinkedHashSet<DrtVariable>();
            this.getAllVariables(thisVars);
            
            DrtLambdaExpression repThat = (DrtLambdaExpression)that.alphaConvert(thisVars);
            if (repThat == null) return false;

            return this.term.equals(repThat.term);
        }
        else 
            return false;
    }
    
    @Override
    public int hashCode() {
        int hc = "lambdaexpression".hashCode();
        if (!(this.term instanceof DrtVariable))
            hc += this.term.hashCode() * 17;
        return hc;
    }
    
    @Override
    public String toString() {
        return "\\" + this.var.toString() + "." + this.term.toString();
    }

    @Override
    public Graph toGraph() {
        return this.term.toGraph();
    }

    @Override
    public DrtLambdaExpression getTemplate(Map<String,String> map) {
        DrtExpression newTerm = this.term.getTemplate(map);
        return new DrtLambdaExpression(var, newTerm);
    }
}
